<?php

class FormSortable
{

  protected $gateway;

  protected $table;

  protected $pk;

  protected $orderField;

  public function __construct($gateway, $orderField)
  {
    $this->gateway = $gateway;
    $this->orderField = $orderField;
    $this->table = $this->gateway->table;
    $this->pk = $this->gateway->pk;

    $action = s::request()->get('formAction');

    if ($action == 'sort') {
      $id = s::request()->get($this->pk);
      $index = s::request()->get('sortIndex');

      $this->move($id, $index);
    }
  }

  public function beforeFindAll(&$params = null)
  {
    $params['orderBy'] = $this->orderField;
  }

  public function beforeInsert(&$data)
  {
    $table = $this->table;
    $pk = $this->pk;
    $orderField = $this->orderField;

    if (isset($data[$orderField])) {
      $new = $data[$orderField];

      DB::getInstance()->query("UPDATE $table SET $orderField = $orderField + 1 WHERE {$this->filter()} AND $orderField >= $new")->execute();
    }
    else {
      $max = DB::getInstance()->query()->from($table)->select("MAX($orderField)")->where($this->filter())->execute()->fetchOne();

      $data[$orderField] = $max + 1;
    }
  }

  public function beforeUpdate(&$data)
  {
    $table = $this->table;
    $pk = $this->pk;
    $orderField = $this->orderField;

    if (isset($data[$orderField])) {
      $id = $data[$pk];

      $old = DB::getInstance()->query()->from($table)->select("$orderField")->where($this->filter())->where("$pk = ?")->execute($id)->fetchOne();

      $new = $data[$orderField];

      if ($new > $old) {
        DB::getInstance()->query("UPDATE $table SET $orderField = $orderField - 1 WHERE {$this->filter()} AND $orderField BETWEEN $old AND $new")->execute();
      }
      elseif ($new < $old) {
        DB::getInstance()->query("UPDATE $table SET $orderField = $orderField + 1 WHERE {$this->filter()} AND $orderField BETWEEN $new AND $old")->execute();
      }
    }
  }

  public function beforeDelete($id, $params = array())
  {
    $table = $this->table;
    $pk = $this->pk;
    $orderField = $this->orderField;

    $old = DB::getInstance()->query()->from($table)->select("$orderField")->where($this->filter())->where("$pk = ?")->execute($id)->fetchOne();

    DB::getInstance()->query("UPDATE $table SET $orderField = $orderField - 1 WHERE {$this->filter()} AND $orderField > $old")->execute();
  }

  public function move($id, $index)
  {
    if (! is_numeric($index) && ! in_array($index, array('top', 'up', 'down', 'bottom', 'first', 'left', 'right', 'last', 'previous', 'next'))) {
      return;
    }

    $pos = DB::getInstance()->query()->from($this->table)->select($this->orderField)->where($this->filter())->where("$this->pk = $id")->execute()->fetchOne();

    $q = DB::getInstance()->query()->from($this->table)->select($this->pk)->where($this->filter());

    if (is_numeric($index)) {
      if ($pos > $index) {
        $data = $q->where("$this->orderField BETWEEN $index AND $pos")->where("$this->pk != $id")->execute()->fetchCol();
        $dif = -1;
        $dis = - count($data);
      }
      else {
        $data = $q->where("$this->orderField BETWEEN $pos AND $index")->where("$this->pk != $id")->execute()->fetchCol();
        $dif = 1;
        $dis = count($data);
      }
    }
    else {
      switch ($index) {
        case 'top' :
        case 'first' :
          $data = $q->where("$this->orderField <= $pos AND $this->pk != $id")->execute()->fetchCol();
          $dif = -1;
          $dis = - count($data);
          break;

        case 'up' :
        case 'left' :
        case 'previous' :
          $dif = -1;
          $dis = -1;
          $data = $q->where("($this->orderField = $pos - 1 OR $this->orderField = $pos) AND $this->pk != $id")->orderBy("$this->orderField DESC")->execute()->fetchCol();
          break;

        case 'down' :
        case 'right' :
        case 'next' :
          $dif = 1;
          $dis = 1;
          $data = $q->where("($this->orderField = $pos + 1 OR $this->orderField = $pos) AND $this->pk != $id")->orderBy("$this->orderField ASC")->execute()->fetchCol();
          break;

        case 'bottom' :
        case 'last' :
          $data = $q->where("$this->orderField >= $pos AND $this->pk != $id")->execute()->fetchCol();
          $dif = 1;
          $dis = count($data);
          break;
      }
    }

    if (! empty($data)) {
      $sql = "UPDATE $this->table SET $this->orderField = GREATEST(0, $this->orderField - $dif) WHERE {$this->filter()} AND $this->pk IN (".implode(', ', $data).")";
      DB::getInstance()->query($sql)->execute();
    }

    if ($pos + $dis != $pos) {
      $sql = "UPDATE $this->table SET $this->orderField = GREATEST(0, $this->orderField + $dis) WHERE {$this->filter()} AND $this->pk = $id";
      DB::getInstance()->query($sql)->execute();
    }
  }

  protected function filter()
  {
    if (! empty($this->gateway->filter)) {
      return $this->gateway->filter;
    }
    else {
      return " TRUE ";
    }
  }

}
